package com.dlyk.service.impl;

import com.dlyk.constant.Constants;
import com.dlyk.exception.PasswordVerifyFailException;
import com.dlyk.manager.RedisListManager;
import com.dlyk.mapper.PermissionMapper;
import com.dlyk.mapper.RoleMapper;
import com.dlyk.mapper.UserMapper;
import com.dlyk.model.Permission;
import com.dlyk.model.Role;
import com.dlyk.model.User;
import com.dlyk.query.BaseQuery;
import com.dlyk.query.UserQuery;
import com.dlyk.service.UserService;
import com.dlyk.util.CacheUtils;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import jakarta.annotation.Resource;
import org.springframework.beans.BeanUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class UserServiceImpl implements UserService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private RoleMapper roleMapper;

    @Resource
    private PermissionMapper permissionMapper;

    @Resource
    private RedisListManager redisListManager;

    /**
     * 密码编码器
     */
    @Resource
    private PasswordEncoder passwordEncoder;

    /**
     * 通过用户名查询用户
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        // 通过用户名查询用户
        User user = userMapper.selectByLoginAct(username);

        if (user == null) {
            throw new UsernameNotFoundException("登录账号不存在");
        }

        // 查询当前用户的角色（数据权限，例如：admin角色可以查看所有的数据，其他角色只能查看自己的（或者部分）数据）
        List<Role> roleList = roleMapper.selectByUserId(user.getId());
        // 字符串的角色列表
        List<String> stringRoleList = new ArrayList<>();
        // 遍历用户的角色集合，获取角色添加到字符串的角色列表
        roleList.forEach(role -> {
            stringRoleList.add(role.getRole());
        });
        // 设置用户的角色（设置数据权限）
        user.setRoleList(stringRoleList);

        // 查询当前用户的菜单权限
        List<Permission> menuPermissionList = permissionMapper.selectMenuPermissionByUserId(user.getId());
        // 设置用户的菜单权限
        user.setMenuPermissionList(menuPermissionList);

        // 查询当前用户的功能权限
        List<Permission> buttonPermissionList = permissionMapper.selectButtonPermissionByUserId(user.getId());
        // 字符串的功能权限
        List<String> stringButtonPermissionList = new ArrayList<>();
        // 遍历用户的功能权限，添加到字符串的功能权限
        buttonPermissionList.forEach(permission -> {
            stringButtonPermissionList.add(permission.getCode());
        });
        // 设置用户的功能权限
        user.setPermissionList(stringButtonPermissionList);

        return user;
    }

    /**
     * 查询用户列表（PageHelper分页查询）
     */
    @Override
    public PageInfo<User> getUserListByPage(Integer current) {

        // 1、设置PageHelper
        PageHelper.startPage(current, Constants.PAGE_SIZE);

        // 2、查询（传入一个BaseQuery类，用于拼接sql语句）
        List<User> userList = userMapper.selectUserListByPage(BaseQuery.builder().build());

        // 3、封装分页数据到PageInfo
        PageInfo<User> userPageInfo = new PageInfo<>(userList);

        return userPageInfo;
    }

    /**
     * 通过id查询用户详情
     */
    @Override
    public User getUserDetailById(Integer id) {
        return userMapper.selectUserDetailById(id);
    }

    /**
     * 添加用户
     */
    @Override
    public int saveUser(UserQuery userQuery, Authentication authentication) {

        User user = new User();

        // 把UserQuery对象里面的属性数据复制到User对象里面
        // 注意：两个对象的属性名和属性类型都要相同，才能复制
        BeanUtils.copyProperties(userQuery, user);

        // 密码加密
        user.setLoginPwd(passwordEncoder.encode(userQuery.getLoginPwd()));
        // 设置创建时间
        user.setCreateTime(new Date());

        // 创建者就是当前登录的用户，从认证信息中获取当前登录用户（或者从token中解析当前登录用户也可以）
        User loginUser = (User) authentication.getPrincipal();
        // 设置创建者
        user.setCreateBy(loginUser.getId());

        return userMapper.insertSelective(user);
    }

    /**
     * 编辑用户
     */
    @Override
    public int editUser(UserQuery userQuery, Authentication authentication) {

        User user = new User();

        // 把UserQuery对象里面的属性数据复制到User对象里面
        // 注意：两个对象的属性名和属性类型都要相同，才能复制
        BeanUtils.copyProperties(userQuery, user);

        // 获取前端提交的密码
        String loginPwd = user.getLoginPwd();
        // 判断密码是否为空，如果不为空，则表示用户修改了密码
        if (StringUtils.hasText(loginPwd)) {
            // 密码验证正则
            String regex = "^\\d{6,12}$";
            // 验证密码是否符合规则
            if (loginPwd.matches(regex)) {
                // 通过验证，密码加密
                String encodeLoginPwd = passwordEncoder.encode(loginPwd);
                // 设置密码
                user.setLoginPwd(encodeLoginPwd);
            } else {
                // 验证未通过，抛出异常，由全局异常处理器处理
                throw new PasswordVerifyFailException("密码验证未通过");
            }
        }
        // 如果密码为空，则表示用户没有修改密码，则不在sql语句中修改密码，<if test="loginPwd != null and loginPwd != ''">

        // 设置编辑时间
        user.setEditTime(new Date());

        // 编辑者就是当前登录的用户，通过认证信息获取当前登录用户
        User loginUser = (User) authentication.getPrincipal();
        // 设置编辑者
        user.setEditBy(loginUser.getId());

        return userMapper.updateByPrimaryKeySelective(user);
    }

    /**
     * 删除用户
     */
    @Override
    public int removeUserById(Integer id) {
        return userMapper.deleteByPrimaryKey(id);
    }

    /**
     * 批量删除用户
     */
    @Override
    public int batchRemoveUserByIds(List<String> ids) {
        return userMapper.deleteByIds(ids);
    }

    /**
     * 查询负责人（带有缓存的查询）
     */
    @Override
    public List<User> getOwnerList() {

        // 1、先从redis查询
        // 2、redis查不到，再从数据库查询，并且把数据放入redis缓存（设置过期时间，例如：5分钟），减少数据库压力

        return CacheUtils.getCacheData(
                () -> {
                    // 生产，从缓存（redis）查询数据
                    return (List<User>) redisListManager.getValue(Constants.REDIS_OWNER_KEY);
                },
                () -> {
                    // 生产，从数据库（mysql）查询数据
                    return userMapper.selectOwnerList();
                },
                (data) -> {
                    // 消费，将数据放入缓存（redis）
                    redisListManager.setValue(Constants.REDIS_OWNER_KEY, data);
                    // 设置过期时间（5分钟）
                    redisListManager.expire(Constants.REDIS_OWNER_KEY, Constants.OWNER_CACHE_EXPIRE_TIME, TimeUnit.SECONDS);
                }
        );
    }

    /**
     * 查询所有合法用户
     */
    @Override
    public List<User> getAllLegitimateUser() {
        return userMapper.selectAllLegitimateUser();
    }

    /**
     * 验证旧密码是否正确（用于修改密码前的验证）
     */
    @Override
    public boolean checkOldPwd(String oldPwd) {

        // 获取当前登录用户对象
        User loginUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        // 获取当前登录用户的密码
        String loginPwd = loginUser.getLoginPwd();

        // 验证用户提交的密码是否正确
        return passwordEncoder.matches(oldPwd, loginPwd);
    }

    /**
     * 修改密码
     */
    @Override
    public int editUserLoginPwd(String newPwd) {

        // 获取当前登录用户
        User loginUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        // 密码加密
        String encodePwd = passwordEncoder.encode(newPwd);
        // 设置新密码
        loginUser.setLoginPwd(encodePwd);

        return userMapper.updateByPrimaryKeySelective(loginUser);
    }

    /**
     * 根据角色id获取用户列表
     */
    @Override
    public PageInfo<User> getUserListByRoleId(Integer roleId, Integer current) {

        // 1、设置PageHelper
        PageHelper.startPage(current, Constants.PAGE_SIZE);

        // 2、查询
        List<User> userList = userMapper.selectUserListByRoleId(roleId);

        // 3、封装分页数据到PageInfo
        PageInfo<User> userPageInfo = new PageInfo<>(userList);

        return userPageInfo;
    }

    /**
     * 获取没有拥有角色的用户集合
     */
    @Override
    public List<User> getNotOwnedRoleUserList() {
        return userMapper.selectNotOwnedRoleUserList();
    }

}

